﻿/*
Copyright 2012 José A. Rojo L.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Reflection;
using System.Globalization;

using Jarol.Console;

namespace jsncsso
{
    class Program
    {
        private static bool     m_bl       = true;
        private static bool     m_bpq      = false;
        private static bool     m_bjs      = false;
        private static char     m_chr      = '\0';
        private static string   m_sym      = " .,;:=/*+-{}()[]<>?&%!|^";

        //----------------------------------------------------------------------------------

        private const string    DUMMY_SRXP = "@__RX__@";
        private const string    DUMMY_SQTE = "@__SQ__@";
        private const string    DUMMY_SCAT = "@__SC__@";

        private const string    DUMMY_FN   = "@__FN__@";                                    // function
        private const string    DUMMY_VR   = "@__VR__@";                                    // var
        private const string    DUMMY_BA   = "@__BA__@";                                    // {
        private const string    DUMMY_BB   = "@__BB__@";                                    // }
        private const string    DUMMY_BC   = "@__BC__@";                                    // (
        private const string    DUMMY_BD   = "@__BD__@";                                    // )
        private const string    DUMMY_BE   = "@__BE__@";                                    // [
        private const string    DUMMY_BF   = "@__BF__@";                                    // ]

        private const  string   WORD_FN    = "function";
        private const  string   WORD_VR    = "var ";
        private const string    SYMBOL_BA  = "{";
        private const string    SYMBOL_BB  = "}";
        private const string    SYMBOL_BC  = "(";
        private const string    SYMBOL_BD  = ")";
        private const string    SYMBOL_BE  = "[";
        private const string    SYMBOL_BF  = "]";

        private static string[] m_arx      = null;
        private static string[] m_asq      = null;

        //----------------------------------------------------------------------------------

        private static bool Pair (int n)
        {
            for (int i = 0; i < 0xFFFFFFF; i += 2)
            {
                if (i == n) return true;
                if (i >  n) break;
            }

            return false;
        }

        //----------------------------------------------------------------------------------

        private static void Cleaner (ref string src)
        {
            byte a = 0;
            byte b = 0;

            for (int i = 0, n, c, j; i < src.Length; ++i)
            {
                switch (src[i])
                {
                	 case '\n':
                        a = b = 0;
                        break;

                    case '\'':
                        c = 0;
                        j = i;
                        while (--j > 0 && src[j] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (a > 0)
                            --a;

                        else if (b == 0)
                            ++a;

                        break;

                    case '"':
                        c = 0;
                        j = i;
                        while (--j > 0 && src[j] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (b > 0)
                            --b;

                        else if (a == 0)
                            ++b;

                        break;

                    case '/':
                        if (a + b == 0 && i + 1 < src.Length)
                        {
                            if (src[i + 1] == '/')
                            {
                                if (i - 1 > 0 && src[i - 1] == ':')
                                    continue;

                                if ((n = src.IndexOf('\n', i)) == -1)
                                    n = src.Length - 1;

                                src = src.Remove(i, ++n - i--);
                            }

                            else if (src[i + 1] == '*')
                            {
                                if (i + 2 < src.Length && src[i + 2] == '@')
                                    continue;

                                if ((n = src.IndexOf("*/", i)) == -1)
                                    n = src.Length - 2;

                                src = src.Remove(i, (n + 2) - i--);
                            }
                        }
                        break;
                }
            }
        }

        //----------------------------------------------------------------------------------

        private static void SetBlockObjects (ref string src, string[] objs, string prefix)
        {
            int l = objs.Length;

            while (l-- > 0)
                src = src.Replace(prefix + l, objs[l]);
        }

        //----------------------------------------------------------------------------------

        private static string[] GetRegExPatterns (ref string src)
        {
            List<string> l = new List<string>();
            int          n = 0;
            byte         a = 0;
            byte         b = 0;

            for (int y = 0, x = -1, i, j, c; y < src.Length; ++y)
            {
                switch (src[y])
                {
                    case '\n':
                        x = -1;
                        a = b = 0;
                        break;

                    case '\'':
                        c = 0;
                        j = y;
                        while (--j > 0 && src[j] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (a > 0)
                            --a;

                        else if (b == 0 && x == -1)
                            ++a;

                        break;

                    case '"':
                        c = 0;
                        j = y;
                        while (--j > 0 && src[j] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (b > 0)
                            --b;

                        else if (a == 0 && x == -1)
                            ++b;

                        break;

                    case '/':
                        if (a + b != 0)
                            continue;

                        if (y > 0 && ("\\/".IndexOf(src[y - 1]) != -1 || (x == -1 && src[y - 1] == '*')))
                            continue;

                        if (y + 1 < src.Length && "/*".IndexOf(src[y + 1]) != -1)
                            continue;

                        i = y;
                        while (--i > 0 && (src[i] == ' ' || src[i] == '\t' || src[i] == '\r' || src[i] == '\n')) ;

                        if (x == -1 && (y == 0 || "=:?{([,".IndexOf(src[i]) != -1  || (--i > 0 && src.Substring(i, 2) == "*/")))
                            x = y;

                        else if (x != -1)
                        {
                            i = y;
                            while (++i > 0 && (src[i] == ' ' || src[i] == '\t')) ;

                            if (y == src.Length - 1 || ",;:)]}.gim\r\n".IndexOf(src[i]) != -1)
                            {
                                l.Add(src.Substring(x, ++y - x));
                                src = src.Remove(x, y - x);
                                src = src.Insert(x, DUMMY_SRXP + n++);
                                y = x;
                                x = -1;
                            }
                        }

                        break;
                }
            }

            return l.ToArray();
        }

        //----------------------------------------------------------------------------------

        private static string[] GetQuotedObjs (ref string src)
        {
            bool         bdq = false;
            bool         bsq = false;
            List<string> lst = new List<string>();
            int          cnt = 0;

            for (int y = 0, x = -1, n, i, c; y < src.Length; ++y)
            {
                switch (src[y])
                {
                    case '"':
                        c = 0;
                        i = y;
                        while (--i > 0 && src[i] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (!bsq)
                        {
                            if ((bdq = !bdq))
                                x = y;

                            if (!bdq && x != -1)
                            {
                                lst.Add(src.Substring(x, n = y - x + 1));
                                x = -1;

								if (lst[lst.Count - 1].IndexOf('\n') != -1)
                                    throw new Exception("Missing double quoted!");
                            }
                        }

                        break;

                    case '\'':
                        c = 0;
                        i = y;
                        while (--i > 0 && src[i] == '\\') ++c;

                        if (!Program.Pair(c))
                            continue;

                        if (!bdq)
                        {
                            if ((bsq = !bsq))
                                x = y;

                            if (!bsq && x != -1)
                            {
                                lst.Add(src.Substring(x, x = y - x + 1));
                                x = -1;

                                if (lst[lst.Count - 1].IndexOf('\n') != -1)
                                    throw new Exception("Missing quoted!");
                            }
                        }

                        break;
                }
            }

            foreach (string s in lst)
                src = src.Replace(s, DUMMY_SQTE + cnt++);

            return lst.ToArray();
        }

        //----------------------------------------------------------------------------------

        private static bool Symbol (char symbol)
        {
            bool b = false;

            foreach (char c in m_sym)
            {
                if (c == ' ')
                    continue;

                if ((b = c == symbol))
                    break;
            }

            return b;
        }

        //----------------------------------------------------------------------------------

        private static string VarInspector (string src)
        {
            int    l = src.Length;
            int    x = -1;
            int    y = -1;

            while (++x < l && (x = y = src.IndexOf(WORD_VR, x)) != -1)
            {
                if (--y > 0 && src[y] == '(')
                    continue;

                y = 0;

                __var_inspector_test:
                while (++x < l && src[x] != ';')
                {
                    if (src[x] == '{') ++y;
                    if (src[x] == '}') --y;
                }

                if (x >= l) break;
                if (y != 0) goto __var_inspector_test;

                if (++x + 4 < l && src.Substring(x, 4) == WORD_VR)
                {
                    src  = src.Remove(--x, 5);
                    src  = src.Insert(x, ",");
                    l   -= 4;

                    goto __var_inspector_test;
                }
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string FnInspector (string src, bool semicolons)
        {
            int  l = src.Length;
            int  x = -1;
            int  y = -1;
            char c;

            while (++x < l && (x = y = src.IndexOf(WORD_FN, x)) != -1)
            {
                int n = 1;

                if (semicolons && !(x - 1 > 0 && src[x - 1] == '='))
                    continue;

                while (++x < l && src[x] != '{') ;

                y = x;
                while (y < l && n != 0)
                {
                    while (++y < l && (c = src[y]) != '}')
                        if (c == '{') ++n;

                    --n;
                }

                if (semicolons)
                {
                    if (++y >= l || ((c = src[y]) != ';' && c != ')' && c != ',' && c != '}'))
                    {
                        src = src.Insert(y > l ? l : y, ";");
                        ++l;
                    }
                }

                else
                {
                    string s = src.Substring(x, y - x);
                    string f = Program.VarInspector(s);

                    if (f.Length < s.Length)
                    {
                        src = src.Replace(s, f);
                        l   = src.Length;
                    }
                }
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string ObjsInspector (string src)
        {
            int n, x, y = -1;
            int l = src.Length;

            while (++y < l && (x = y = src.IndexOf('}', y)) != -1)
            {
                if (++y < l && src[y] != ';')
                {
                    n = 1;
                    while (n != 0 && x > 0)
                    {
                        while (--x > 0 && src[x] != '{')
                            if (src[x] == '}') ++n;

                        --n;
                    }

                    if (--x > 0 && src[x] == ')')
                        continue;

                    if (x - 1 > -1 && src.Substring(x - 1, 2) == "do")
                        continue;

                    if (x - 2 > -1 && src.Substring(x - 2, 3) == "try")
                        continue;

                    if (!Program.Symbol(src[y]))
                    {
                        src = src.Insert(y, ";");
                        ++l;
                    }
                }
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string RoundBracketsInspector (string src)
        {
            int  n, x, y = -1;
            int  l = src.Length;
            bool b;

            while (++y < l && (x = y = src.IndexOf(')', y)) != -1)
            {
                if (++y < l && src[y] != ';')
                {
                    n = 1;
                    while (n != 0 && x > 0)
                    {
                        while (--x > 0 && src[x] != '(')
                            if (src[x] == ')') ++n;

                        --n;
                    }

                    if (--x > 0)
                    {
                        b = false;
                        foreach (string s in new string[] { "if", "for", "while", "function" })
                        {
                            if (x - (n = s.Length - 1) > -1 && (b = src.Substring(x - n, ++n) == s))
                            {
                                if (s[0] != 'w')
                                    break;

                                else if ((x -= n) > 0 && src[x] == '}')
                                {
                                    n = 1;
                                    while (n != 0)
                                    {
                                        while (src[--x] != '{')
                                            if (src[x] == '{') ++n;

                                        --n;
                                    }

                                    b = !(src.Substring(x - 2, 2) == "do");
                                }
                            }
                        }

                        if (b) continue;

                        if (!Program.Symbol(src[y]))
                        {
                            src = src.Insert(y, ";");
                            ++l;
                        }
                    }
                }
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string SquareBracketsInspector (string src)
        {
            int y = -1;
            int l = src.Length;

            while (++y < l && (y = src.IndexOf(']', y)) != -1)
            {
                if (++y < l && src[y] != ';')
                {
                    if (!Program.Symbol(src[y]))
                    {
                        src = src.Insert(y, ";");
                        ++l;
                    }
                }
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string HexNumInspector (string src)
        {
            Regex           rx = new Regex((m_bjs ? "0[xX]" : "#") + "[0-9a-fA-F]+");
            MatchCollection mc = rx.Matches(src);
            string          s;

            for (int i = 0; i < mc.Count; ++i)
            {
                if (m_bjs)
                {
                    s = UInt64.Parse(mc[i].Value.Substring(2), NumberStyles.HexNumber).ToString();

                    if (s.Length <= mc[i].Value.Length)
                    {
                        src = src.Remove(mc[i].Index, mc[i].Value.Length);
                        src = src.Insert(mc[i].Index, s);
                        mc = rx.Matches(src);
                        i = 0;
                    }
                }

                else if ((s = mc[i].Value.ToLower()).Length == 7)
                    if (s[1] == s[2] && s[3] == s[4] && s[5] == s[6])
                        src = src.Replace(mc[i].Value, "#" + s[1] + s[3] + s[5]);
            }

            return src;
        }

        //----------------------------------------------------------------------------------

        private static string FloatNumInspector (string src)
        {
            foreach (Match m in new Regex(@"0+\.(\d+)").Matches(src))
                src = src.Replace(m.Value, m.Value.Substring(m.Value.IndexOf('.')));

            return src;
        }

        //----------------------------------------------------------------------------------

        private static int IndexOfSym (string src, string sym, int len = -1)
        {
            int i = -1;
            int n = sym.Length;

            if (len == -1)
                len = src.Length;

            while ((i = src.IndexOf(sym, ++i)) != -1)
            {
                if (i == 0) continue;

                if (i + n >= len)
                {
                    i = -1;
                    break;
                }

                char x = src[i - 1];
                char y = src[i + n];

                if (x == '.' || (y == ':' && (x == '{' || x == ',')))
                    continue;

                bool b = false;
                bool e = false;

                foreach (char c in m_sym)
                {
                    if (!b) b = x == c;
                    if (!e) e = y == c;

                    if (b && e)
                        break;
                }

                if (b && e) break;
            }

            return i;
        }

        //----------------------------------------------------------------------------------

        private static string ParseLn (string src)
        {
            src = src.Replace("\t", string.Empty);

            foreach (char c in m_sym)
            {
                string s = c.ToString();
                string f = s + ' ';
                int    n = -1;
                int    x;
                int    l;   

                while ((x = n = src.IndexOf(f, ++n)) != -1)
                {
                    if (c == '-' || c == '+')                                                   // -> n - -i | n + +i | n - +i | ...
                    {
                        l = src.Length;
                        while (++x < l && src[x] == ' ') ;

                        if (x < l && (src[x] == '-' || src[x] == '+'))
                            continue;
                    }

                    src = c == ' ' ? src.Remove(n--, 2) : src.Remove(n-- + 1, 1);
                }

                n = -1;
                f = ' ' + s;
                while ((x = n = src.IndexOf(f, ++n)) != -1)                                   
                {
                    if (c == '-' || c == '+')                                                    // -> n - -i | n + +i | n - +i | ...
                    {
                        while (--x > 0 && src[x] == ' ');

                        if (src[x] == '-' || src[x] == '+')
                            continue;
                    }

                    src = src.Remove(n--, c == ' ' ? 2 : 1);
                }
            }

            src = Program.FloatNumInspector(Program.HexNumInspector(src)).Trim();

            if (src.Length < 1)
                return src;

            if (m_chr != '\0')
            {
                foreach (char c in m_sym)
                {
                    if (c == ';' || c == ' ')
                        continue;

                    if (c == m_chr || c == src[0])
                        m_chr = '\0';
                }

                if (m_chr != '\0')
                    src = ';' + src;
            }

            char z = src[src.Length - 1];
            m_chr = z == ';' ? '\0' : z;

            return src;
        }

        //----------------------------------------------------------------------------------

        private static void ShowLogo()
        {
            if (m_bl)
            {
                Version v = Assembly.GetExecutingAssembly().GetName().Version;
                Messenger.Print
                (
                      Messenger.Frame.SIMPLE
                    , "JavaScript and Cascading Style Sheets Optimizer Tool v." + v.Major +
                      "." + v.Minor + "." + v.Build + "." + v.Revision + "\n" +
                      "Copyright (c) 2012 José A. Rojo L."
                    , ConsoleColor.DarkGreen
                    , ConsoleColor.DarkGreen
                    , true
                );

                m_bl = false;
            }
        }

        //----------------------------------------------------------------------------------

        private static void ShowHelp()
        {
            Program.ShowLogo();
            Messenger.Print
            (
                  "\n Usage:\n\n\t\rjsncsso [options...] filename.<js|css>\n"                       +
                  "\n \rOptions:\n\n"                                                               +
                  "\t\r-q  --preserve-quotations \rPreserve original content of literal"            +
                  "\n\t\t\t\t  strings (\"...\" and '...').\n\n"                                    +
                  "\t\r-m  --minify-names        \rMinify names of variables and arguments"         +
                  "\n\t\t\t\t  into functions (js files only).\n\n"                                 +
                  "\t\r-d  --disable-str-cat     \rDisable string concatenation (js files only).\n" +
                  "\t\r-o  --output-file         \rOutput file name (min-filename By default).\n"   +
                  "\t\r-w  --overwrite           \rOverwrites the existing output file(s)"          +
                  "\n\t\t\t\t  without asking.\n\n"                                                 +
                  "\t\r-h  --help                \rShow this screen.\n"                             +
                  "\n \rSamples:\n\n"                                                               +
                  "\t\r<1>\r jsncsso myfile.js\n"                                                   +
                  "\t\r<2>\r jsncsso myfile.css -q\n"                                               +
                  "\t\r<3>\r jsncsso -q -o min-myfile.js myfile.js\n"                               +
                  "\t\r<4>\r jsncsso -m *.js\n"

                , new ConsoleColor[]
                  {
                        ConsoleColor.Yellow
                      , ConsoleColor.Gray

                      , ConsoleColor.Yellow

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.Yellow

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray

                      , ConsoleColor.White
                      , ConsoleColor.Gray
                  }
            );
        }

        //----------------------------------------------------------------------------------

        static void Main (string[] args)
        {
            int l = args.Length;

            if (l < 1)
                Program.ShowHelp();

            else
            {
                try
                {
                    string sfi = string.Empty;
                    string sfo = string.Empty;
                    bool   bow = false;
                    bool   bsc = true;
                    bool   bmn = false;
                    int    nfi = -1;
                    int    nfo = -1;

                    for (int i = 0; i < l; ++i)
                    {
                        switch (args[i].ToLower())
                        {
                            case "-q":
                            case "--preserve-quotations":
                                m_bpq = true;
                                break;

                            case "-d":
                            case "--disable-str-cat":
                                bsc = false;
                                break;

                            case "-m":
                            case "--minify-names":
                                bmn = true;
                                break;

                            case "-o":
                            case "--output-file":
                                sfo = args[nfo = ++i];
                                break;

                            case "-w":
                            case "--overwrite":
                                bow = true;
                                break;

                            case "-h":
                            case "--help":
                                Program.ShowHelp();
                                Environment.Exit(0);
                                break;

                            default:
                                sfi = args[nfi = i];
                                break;
                        }
                    }

                    Program.ShowLogo();

                    if (File.Exists(sfi))
                    {
                        Messenger.Print
                        (
                              Messenger.Icon.INFORMATION
                            , "Optimizing, please wait... "
                        );

                        StreamReader fsr = new StreamReader(sfi);
                        string       sln = string.Empty;
                        string       src = fsr.ReadToEnd();
                        int          nbp = -1;
                        int          nep = -1;
                        char         c;

                        fsr.Close();

                        m_bjs = !(Path.GetExtension(sfi).ToLower() == ".css");

                        if (m_bjs)
                            m_arx = Program.GetRegExPatterns(ref src);

                        Program.Cleaner(ref src);

                        if (m_bpq)
                            m_asq = Program.GetQuotedObjs(ref src);

                        if (!m_bjs)
                        {
                            string[] shc;
                            foreach (Match m in new Regex(@"rgb\s*\([\d,\s]*\)").Matches(src))
                            {
                                nbp = m.Value.IndexOf('(');
                                nep = m.Value.IndexOf(')');
                                shc = m.Value.Substring(++nbp, nep - nbp).Split(new char[] {','});

                                for (nbp = 0, nep = shc.Length; nbp < nep; ++nbp)
                                    shc[nbp] = int.Parse(shc[nbp].Trim()).ToString("X2");

                                src = src.Replace(m.Value, '#' + String.Join(string.Empty, shc));
                            }
                        }

                        fsr = new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(src)));
                        src = string.Empty;

                        while ((sln = fsr.ReadLine()) != null)
                            src += Program.ParseLn(sln);

                        fsr.Close();
 
                        if (m_bjs)
                        {
                            if (!m_bpq)
                            {
                                m_asq = Program.GetQuotedObjs(ref src);

                                if (bmn) for (nbp = 0, nep = m_asq.Length; nbp < nep; ++nbp)
                                {
                                    m_asq[nbp] = m_asq[nbp].Replace(WORD_FN, DUMMY_FN);
                                    m_asq[nbp] = m_asq[nbp].Replace(WORD_VR, DUMMY_VR);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BA, DUMMY_BA);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BB, DUMMY_BB);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BC, DUMMY_BC);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BD, DUMMY_BD);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BE, DUMMY_BE);
                                    m_asq[nbp] = m_asq[nbp].Replace(SYMBOL_BF, DUMMY_BF);
                                }
                            }

                            if (bsc)
                                foreach (Match m in new Regex(DUMMY_SQTE + @"\d+\+").Matches(src))
                                    src = src.Replace(m.Value, m.Value.Replace("+", DUMMY_SCAT));

                            src = Program.FnInspector(src, true);
                            src = Program.ObjsInspector(src);
                            src = Program.RoundBracketsInspector(src);
                            src = Program.SquareBracketsInspector(src);
                            src = Program.FnInspector(src, false);
                            src = Program.VarInspector(src);

                            while ((nbp = src.IndexOf(";;")) != -1)
                                src = src.Remove(nbp, 1);

                            sln = "case";
                            src = src.Replace(sln + " " + DUMMY_SQTE, sln + DUMMY_SQTE);

                            if (!m_bpq)
                                Program.SetBlockObjects(ref src, m_asq, DUMMY_SQTE);

							l = src.Length;

                            if (bmn)
                            {
                                m_sym += "\"'";
                                nbp    = -1;

                                if (bsc) 
                                    m_sym += '@';

                                while (++nbp < l && (nbp = src.IndexOf(WORD_FN, nbp)) != -1)
                                {
                                    string sfn = string.Empty;

                                    nbp += 7;
                                    while (++nbp < l && src[nbp] != '(')
                                        sfn += src[nbp];

                                    if (nbp >= l)
                                        break;

                                    nep = nbp;
                                    while (++nep < l && src[nep] != ')') ;

                                    if (nep >= l)
                                        break;

                                    int nix;
                                    string[] fag = { };

                                    if (nep > nbp + 1)
                                    {
                                        fag = src.Substring(++nbp, nep - nbp).Split
                                        (
                                              new char[] { ',' }
                                            , StringSplitOptions.RemoveEmptyEntries
                                        );
                                    }

                                    while (++nep < l && src[nep] != '{') ;

                                    if (nep >= l)
                                        break;

                                    nix = 1;
                                    while (nix != 0)
                                    {
                                        try
                                        {
                                            while (src[++nep] != '}')
                                                if (src[nep] == '{')
                                                    ++nix;

                                            --nix;
                                        }

                                        catch (Exception)
                                        {
                                            throw new Exception("Missing }.");
                                        }
                                    }

                                    if (sfn.Length == 2)
                                       nbp -= sfn.Length;

                                    sfn = src.Substring(fag.Length > 0 ? --nbp : nbp, ++nep - nbp);

                                    int          nln, cnt;
                                    int          fln = sfn.Length;
                                    List<string> lst = new List<string>();
                                    string       stp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
                                    bool         bin;

                                    for (int i = 0; i < stp.Length; ++i)
                                        if (Program.IndexOfSym(sfn, stp[i].ToString(), fln) != -1)
                                            stp = stp.Remove(i--, 1);

                                    if ((nln = stp.Length) == 0)
                                        continue;

                                    foreach (string s in fag)
                                    {
                                        if (lst.Count >= nln)
                                            break;

                                        if (s.Length > 1)
                                            lst.Add(s);
                                    }

                                    foreach (Match m in new Regex(WORD_FN + @" \w+\(").Matches(sfn))
                                    {
                                        if (lst.Count >= nln)
                                            break;

                                        string s = m.Value.Substring(9, m.Value.Length - 10);

                                        if (s.Length > 1)
                                            lst.Add(s);
                                    }

                                    nix = -1;
                                    l   = sfn.Length;

                                    while ((nix = sfn.IndexOf(WORD_VR, ++nix)) != -1)
                                    {
                                        if ((nix += 4) >= l)
                                            break;

                                        nep = nix;
                                        cnt = 0;

                                        __main_var_separator:

                                        bin = false;
                                        while (++nep < l)
                                        {
                                            if (!m_bpq)
                                            {
                                                int esc;
                                                int idx; 

                                                if (sfn[nep] == '"')
                                                {
                                                    __main_dq_ctl:
                                                    while (++nep < l && sfn[nep] != '"') ;

                                                    if (nep >= l)
                                                        break;

                                                    esc = 0;
                                                    idx = nep;

                                                    while (sfn[--idx] == '\\') ++esc;

                                                    if (!Program.Pair(esc))
                                                        goto __main_dq_ctl;

                                                    if (++nep >= l)
                                                        break;
                                                }

                                                else if (sfn[nep] == '\'')
                                                {
                                                    __main_sq_ctl:
                                                    while (++nep < l && sfn[nep] != '\'') ;

                                                    if (nep >= l)
                                                        break;

                                                    esc = 0;
                                                    idx = nep;

                                                    while (sfn[--idx] == '\\') ++esc;

                                                    if (!Program.Pair(esc))
                                                        goto __main_sq_ctl;

                                                    if (++nep >= l)
                                                        break;
                                                }
                                            }

                                            c = sfn[nep];

                                            if (c == '(' || c == '[' || c == '{')
                                                ++cnt;

                                            else if (c == ')' || c == ']' || c == '}')
                                                --cnt;

                                            else if (cnt == 0 && (c == '=' || c == ';' || c == ','))
                                                break;

                                            else if (cnt == 0 && nep + 3 < l && sfn.Substring(nep, 4) == " in ")
                                            {
                                                bin = true;
                                                break;
                                            }
                                        }

                                        if (nep < l)
                                        {
											if (lst.Count >= nln)
                                                break;

                                            string svn = sfn.Substring(nix, nep - nix);

                                            if (svn[0] != '=' && svn.Length > 1)
                                                lst.Add(svn);

                                            c = sfn[nix = nep];

                                            if (bin)      c = ';';
                                            if (c == ',') nep = ++nix;
                                            if (c != ';') goto __main_var_separator;
                                        }
                                    }

                                    cnt = 0;
                                    foreach (string s in lst)
                                    {
                                        if (s.IndexOf(' ') != -1) throw new Exception
                                        (
                                            "Missing parameter name. The resulting code will be useless!"
                                        );

                                        c = stp[cnt++];
                                        while ((nix = Program.IndexOfSym(sfn, s)) != -1)
                                        {
                                            sfn = sfn.Remove(nix, s.Length);
                                            sfn = sfn.Insert(nix, c.ToString());
                                        }
                                    }

                                    if (fln != sfn.Length)
                                    {
                                        src = src.Remove(nbp, fln);
                                        src = src.Insert(nbp, sfn);
                                    }

                                    l = src.Length;
                                }
                            }

                            foreach (string s in new string[] { "cc_on", "cc_off", "else", "end" })
                            {
                                nbp = -1;
                                while ((nbp = src.IndexOf('@' + s, nbp + 1)) != -1)
                                    if ((nbp += +s.Length + 1) < l && src[nbp] != ' ')
                                        src = src.Insert(nbp, " ");
                            }

                            nbp = -1;
                            while ((nbp = src.IndexOf("@*/", nbp + 1)) != -1)
                                src = src.Insert(nbp = nbp + 3, "\n");
                        }

                        else
                        {
                            string[] spn =
                            {
                                  "outline"
                                , "background"
                                , "border"
                                , "border-top"
                                , "border-left"
                                , "border-right"
                                , "border-bottom"
                            };

                            sln = ":0";
                            foreach (string s in spn)
                                src = src.Replace(s + ":none", s + sln);

                            spn = new string[] { "px", "em", "%", "in", "cm", "mm", "pc", "pt", "ex" };
                            foreach (string s in spn)
                                src = src.Replace(sln + s, sln);
                        }

                        src = src.Replace(";}", "}");

                        if (m_bjs)
                        {
                            foreach (string s in new string[] { "for", "while" })
                            {
                                nbp = -1;
                                while ((nbp = src.IndexOf(s, ++nbp)) != -1)
                                {
                                    if (src[nbp += s.Length] == '(')
                                    {
                                        nep = 1;
                                        while (nep != 0)
                                        {
                                            try
                                            {
                                                while (src[++nbp] != ')')
                                                    if (src[nbp] == '(')
                                                        ++nep;

                                                --nep;
                                            }

                                            catch (Exception)
                                            {
                                                throw new Exception("Missing ).");
                                            }
                                        }

                                        if (src[++nbp] == '}')
                                            src = src.Insert(nbp, ";");
                                    }
                                }
                            }
                        }

                        if (m_bpq)
                            Program.SetBlockObjects(ref src, m_asq, DUMMY_SQTE);

                        if (m_bjs)
                        {
                            if (bsc)
                            {
                                src = src.Replace("\"" + DUMMY_SCAT + "\"", string.Empty);
                                src = src.Replace("'" + DUMMY_SCAT + "'", string.Empty);
                                src = src.Replace(DUMMY_SCAT, "+");
                            }

                            if (!m_bpq && bmn)
                            {
                                src = src.Replace(DUMMY_FN, WORD_FN);
                                src = src.Replace(DUMMY_VR, WORD_VR);
                                src = src.Replace(DUMMY_BA, SYMBOL_BA);
                                src = src.Replace(DUMMY_BB, SYMBOL_BB);
                                src = src.Replace(DUMMY_BC, SYMBOL_BC);
                                src = src.Replace(DUMMY_BD, SYMBOL_BD);
                                src = src.Replace(DUMMY_BE, SYMBOL_BE);
                                src = src.Replace(DUMMY_BF, SYMBOL_BF);
                            }

                            Program.SetBlockObjects(ref src, m_arx, DUMMY_SRXP);
                        }

                        if (sfo.Length == 0)
                            sfo = Path.GetDirectoryName(Path.GetFullPath(sfi)) +
                                  Path.DirectorySeparatorChar +
                                  "min-" + Path.GetFileName(sfi);

                        if (File.Exists(sfo))
                        {
                            if (!bow)
                            {
                                bow = ConsoleKey.Y == Messenger.Print
                                (
                                      Messenger.Icon.QUESTION
                                    , String.Format
                                      (
                                            "The file \"{0}\"\n" +
                                            "     Already exists! Overwrite?"
                                          , Path.GetFullPath(sfo)
                                      )
                                    , new ConsoleKey[] { ConsoleKey.Y, ConsoleKey.N }
                                    , true
                                    , true
                                );
                            }

                            if (bow)
                                File.Delete(sfo);

                            else
                            {
                                Messenger.Print
                                (
                                      Messenger.Icon.WARNING
                                    , "Process canceled by user!\n"
                                );

                                goto __main_eof; //Environment.Exit(0);
                            }
                        }

                        StreamWriter fsw = new StreamWriter(sfo);
                        fsw.Write(src);
                        fsw.Close();

                        Messenger.Print
                        (
                              Messenger.Icon.INFORMATION
                            , "Done!\n"
                        );
                    }

                    else
                    {
                        if (nfi == -1)
                            goto __main_throw_file_no_found;

                        char[]   wildcards = { '*', '?' };
                        string   pattern   = Path.GetFileName(sfi);
                        string[] files     = null;
                        string   path      = sfi.Substring(0, sfi.IndexOf(pattern));

                        if (path.Length == 0)
                            path = "." + Path.DirectorySeparatorChar;

                        foreach (char c in wildcards)
                        {
                            if (pattern.IndexOf(c) != -1)
                            {
                                files = Directory.GetFiles
                                (
                                      path
                                    , pattern
                                    , SearchOption.TopDirectoryOnly
                                );

                                break;
                            }
                        }

                        if (files != null && files.Length > 0)
                        {
                            if (nfo > -1)
                            {
                                bow = ConsoleKey.Y == Messenger.Print
                                (
                                      Messenger.Icon.QUESTION
                                    , "The output file name will be ignored when " +
                                      "dealing with multiple files.\n     It will" +
                                      " set the default names instead (min-input_" +
                                      "file_name.ext).\n     Continue?"
                                    , new ConsoleKey[] { ConsoleKey.Y, ConsoleKey.N }
                                    , true
                                    , true
                                );

                                if (!bow)
                                {
                                    Messenger.Print
                                    (
                                          Messenger.Icon.WARNING
                                        , "Process canceled by user!\n"
                                    );

                                    Environment.Exit(0);
                                }

                                args[nfo] = string.Empty;
                            }

                            foreach (string file in files)
                            {
                                args[nfi] = file;

                                Messenger.Print
                                (
                                      Messenger.Icon.INFORMATION
                                    , "Processing \"" + file + "\"."
                                );

                                Program.Main(args);
                            }

                            Environment.Exit(0);
                        }

                        __main_throw_file_no_found:

                        throw new Exception
                        (
                            String.Format
                            (
                                  "File(s) \"{0}\" no found!"
                                , sfi
                            )
                        );
                    }

                    __main_eof:
                    Console.Write(string.Empty);
                }

                catch (Exception e)
                {
                    Program.ShowLogo();

                    if (e.Message.Length > 0)
                        Messenger.Print(Messenger.Icon.ERROR, e.Message, false, true);

                    Environment.Exit(1);
                }
            }
        }
    }
}
